package distributeFiles

import (
	"context"
	v1 "devops-super/api/clusters/v1"
	"devops-super/internal/consts"
	"devops-super/internal/service"
	"devops-super/utility/util/appUtil"
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/gogf/gf/v2/frame/g"
	"syscall"
	"time"
)

type sDistributeFiles struct{}

func init() {
	service.RegisterDistributeFiles(New())
}

func New() *sDistributeFiles {
	return &sDistributeFiles{}
}

// GetDirList 获取根目录列表
func (s *sDistributeFiles) GetDirList(ctx context.Context, serverKey string) (res *v1.DistributeFilesGetDirListRes, err error) {
	// 根据serverKey获取数据盘路径
	rootDir, err := s.getDataDiskPath(ctx, serverKey)
	if err != nil {
		return nil, err
	}
	res = &v1.DistributeFilesGetDirListRes{}
	res.List, err = s.scanDir(ctx, rootDir)
	return
}

// GetSubDirList 获取子目录列表
func (s *sDistributeFiles) GetSubDirList(ctx context.Context, serverKey, path string) (res *v1.DistributeFilesGetSubDirListRes, err error) {
	// 根据serverKey获取数据盘路径
	rootDir, err := s.getDataDiskPath(ctx, serverKey)
	if err != nil {
		return nil, err
	}
	res = &v1.DistributeFilesGetSubDirListRes{}
	// 安全检查：防止路径遍历攻击
	fullPath := filepath.Join(path)
	// 使用标准库检查文件是否存在
	_, err = os.Stat(fullPath)
	if os.IsNotExist(err) {
		return nil, errors.New("目录不存在")
	}
	if err != nil {
		return nil, err
	}

	// 检查是否在根目录范围内
	if !strings.Contains(fullPath, filepath.Join(rootDir)) {
		return nil, errors.New("访问权限不足")
	}

	res.List, err = s.scanDir(ctx, fullPath)
	return
}

// DeleteDir 删除目录
func (s *sDistributeFiles) DeleteDir(ctx context.Context, serverKey, path string) (err error) {
	isSuperDept, _, _ := service.User().IsCurrentUserSuperDept(ctx)
	if !isSuperDept {
		err = errors.New("permission deny")
		return
	}
	// 根据serverKey获取数据盘路径
	rootDir, err := s.getDataDiskPath(ctx, serverKey)
	if err != nil {
		return err
	}
	// 安全检查：防止路径遍历攻击
	fullPath := filepath.Join(path)
	// 使用标准库检查文件是否存在
	_, err = os.Stat(fullPath)
	if os.IsNotExist(err) {
		return errors.New("目录不存在")
	}
	if err != nil {
		return err
	}

	// 检查是否在根目录范围内
	if !strings.Contains(fullPath, filepath.Join(rootDir)) {
		return errors.New("没有删除权限")
	}

	// 删除目录及其所有内容
	return os.RemoveAll(fullPath)
}

// 根据serverKey获取数据盘路径
func (s *sDistributeFiles) getDataDiskPath(ctx context.Context, serverKey string) (string, error) {
	ckey := consts.EnvConfig_CurrentServer
	cString, err := g.Cfg().Get(ctx, ckey)
	if err != nil {
		return "", err
	}
	if cString.String() != serverKey {
		err = errors.New("current server key not match")
	}
	separator := "/"
	docDir, _ := appUtil.GetRootDir()
	path := docDir + separator + consts.AppDataDir
	return path, nil
}

// scanDir 扫描目录并返回文件信息列表
func (s *sDistributeFiles) scanDir(ctx context.Context, dirPath string) (fileInfos []*v1.FileInfo, err error) {
	entries, err := os.ReadDir(dirPath)
	if err != nil {
		g.Log().Error(ctx, "扫描目录失败:", err)
		return nil, errors.New("获取目录信息失败")
	}

	fileInfos = make([]*v1.FileInfo, 0, len(entries))
	for _, entry := range entries {
		info, err := entry.Info()
		if err != nil {
			g.Log().Error(ctx, "获取文件信息失败:", err)
			continue
		}

		// 构造文件信息
		fileInfo := &v1.FileInfo{
			Name:         info.Name(),
			Path:         filepath.Join(dirPath, info.Name()),
			IsDir:        entry.IsDir(),
			Size:         info.Size(),
			ModTime:      info.ModTime().Format("2006-01-02 15:04:05"),
			CreationTime: getCreationTime(info),
		}

		// 如果是目录，计算目录总大小
		//if entry.IsDir() {
		//	fileInfo.Size, _ = s.getDirSize(filepath.Join(dirPath, info.Name()))
		//}

		fileInfos = append(fileInfos, fileInfo)
	}

	return fileInfos, nil
}

// getDirSize 计算目录总大小
func (s *sDistributeFiles) getDirSize(dirPath string) (int64, error) {
	var size int64
	err := filepath.WalkDir(dirPath, func(path string, entry os.DirEntry, err error) error {
		if err != nil {
			return err
		}

		if !entry.IsDir() {
			info, err := entry.Info()
			if err != nil {
				return err
			}
			size += info.Size()
		}
		return nil
	})
	return size, err
}

// getCreationTime 获取文件创建时间（跨平台兼容处理）
func getCreationTime(info os.FileInfo) string {
	// 在Windows上获取创建时间
	if sys := info.Sys(); sys != nil {
		if winFileInfo, ok := sys.(*syscall.Win32FileAttributeData); ok {
			// 将Windows文件时间转换为Unix时间
			ctime := time.Unix(0, winFileInfo.CreationTime.Nanoseconds())
			return ctime.Format("2006-01-02 15:04:05")
		}
	}
	// 其他系统使用修改时间
	return info.ModTime().Format("2006-01-02 15:04:05")
}
